package com.len.template;

import com.len.redisson.RedissonDistributedLocker;
import com.len.redisson.event.req.RedisUnlockEventReq;
import com.len.redisson.event.req.RedisUnlockTransactionalEventReq;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class RedisLockTemplate {

    @Resource
    private RedissonDistributedLocker redissonDistributedLocker;

    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    /**
     * 默认带事务
     * @param lockKey
     * @param supplier
     * @param <T>
     * @throws Exception
     */
    public <T> T call(String lockKey, BizProcess<T> supplier) throws Exception {
        return call(false, lockKey, supplier);
    }

    /**
     *
     * @param isTransactional 是否带事务操作
     * @param lockKey
     * @param supplier
     * @param <T>
     * @throws Exception
     */
    public <T> T call(boolean isTransactional, String lockKey, BizProcess<T> supplier) throws Exception {
        long startTime = System.currentTimeMillis();
        boolean lockSuccess = false;
        try {
            //加锁
            lockSuccess = redissonDistributedLocker.tryLock(lockKey);
            if (!lockSuccess) {
                throw new RuntimeException("无法获取交易锁!");
            }

            //执行业务
            if (supplier != null) {
                return supplier.process();
            } else {
                return null;
            }
        } finally {
            if (isTransactional) {
                applicationEventPublisher.publishEvent(new RedisUnlockTransactionalEventReq(lockSuccess, startTime, lockKey));
            } else {
                applicationEventPublisher.publishEvent(new RedisUnlockEventReq(lockSuccess, startTime, lockKey));
            }
        }
    }

    @FunctionalInterface
    public interface BizProcess<T> {

        /**
         * Gets a result.
         *
         * @return a result
         * @throws Exception
         */
        T process() throws Exception;
    }
}
